// import com.alibaba.druid.DbType;
// import com.alibaba.druid.FastsqlException;
// import com.alibaba.druid.sql.SQLUtils;
// import com.alibaba.druid.sql.ast.SQLDataType*;
// import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
// import com.alibaba.druid.sql.ast.SQLExpr;
// import com.alibaba.druid.sql.parser.ParserException;

// import java.math.BigDecimal;
// import java.math.BigInteger;
// import java.sql.Types;
// import java.util.Date;
// import java.util.std::list;
// import java.util.TimeZone;
#include "SQLExprUtils.h"
#include "../SQLExpr.h"
#include "SQLPropertyExpr.h"
#include "SQLIdentifierExpr.h"
#include "SQLBinaryOpExpr.h"
#include "SQLLiteralExpr.h"
#include "SQLInListExpr.h"
#include "../../../utils/instanceof.h"
#include "../../SQLUtils.h"
#include "SQLCharExpr.h"
#include "../../../Exception/FastsqlException.h"
#include "../../../utils/Types.h"
#include "../SQLDataTypeImpl.h"

bool SQLExprUtils::equals(SQLExpr_ptr a, SQLExpr_ptr b)
{
  if (a == b)
  {
    return true;
  }

  if (a == nullptr || b == nullptr)
  {
    return false;
  }

  string_ptr clazz_a = a->getClass();
  string_ptr clazz_b = b->getClass();

  if (*clazz_a == *SQLPropertyExpr::class_ && *clazz_b == *SQLIdentifierExpr::class_)
  {
    return (std::dynamic_pointer_cast<SQLPropertyExpr>(a))->equals(std::dynamic_pointer_cast<SQLIdentifierExpr>(b));
  }

  if (clazz_a != clazz_b)
  {
    return false;
  }

  if (clazz_a == SQLIdentifierExpr::class_)
  {
    SQLIdentifierExpr_ptr x_a = std::dynamic_pointer_cast<SQLIdentifierExpr>(a);
    SQLIdentifierExpr_ptr x_b = std::dynamic_pointer_cast<SQLIdentifierExpr>(b);
    return x_a->hashCode() == x_b->hashCode();
  }

  if (clazz_a == SQLBinaryOpExpr::class_)
  {
    SQLBinaryOpExpr_ptr x_a = std::dynamic_pointer_cast<SQLBinaryOpExpr>(a);
    SQLBinaryOpExpr_ptr x_b = std::dynamic_pointer_cast<SQLBinaryOpExpr>(b);

    return x_a->equals(x_b);
  }

  return a->equals(b);
}

BOOL_ptr SQLExprUtils::isLiteralExpr(SQLExpr_ptr expr)
{
  if (instanceof <SQLExpr, SQLLiteralExpr>(expr))
  {
    return BOOL::TRUE;
  }

  if (instanceof <SQLExpr, SQLBinaryOpExpr>(expr))
  {
    SQLBinaryOpExpr_ptr binary = std::dynamic_pointer_cast<SQLBinaryOpExpr>(expr);
    return (isLiteralExpr(binary->left) && isLiteralExpr(binary->right)) ? BOOL::TRUE : BOOL::FALSE;
  }

  return BOOL::FALSE;
}

SQLExpr_ptr SQLExprUtils::fromJavaObject(Object_ptr o)
{
  return fromJavaObject(o, 0);
}

SQLExpr_ptr SQLExprUtils::fromJavaObject(Object_ptr o, time_t timeZone)
{
  SQLExpr_ptr tmp = SQLExpr_ptr(new SQLExpr());
  return tmp;
  // if (o == nullptr) {
  //     return new SQLNullExpr();
  // }

  // if (o instanceof string_ptr ) {
  //     return new SQLCharExpr((string_ptr ) o);
  // }

  // if (o instanceof BigDecimal) {
  //     return new SQLDecimalExpr((BigDecimal) o);
  // }

  // if (o instanceof Byte || o instanceof Short || o instanceof int || o instanceof Long || o instanceof BigInteger) {
  //     return new SQLIntegerExpr((Number) o);
  // }

  // if (o instanceof Number) {
  //     return new SQLNumberExpr((Number) o);
  // }

  // if (o instanceof Date) {
  //     return new SQLTimestampExpr((Date) o, timeZone);
  // }

  // throw new ParserException("not support class : " + o.getClass());
}

SQLInListExpr *SQLExprUtils::conditionIn(string_ptr column, Object_list_ptr values, time_t timeZone)
{
  SQLInListExpr *in = new SQLInListExpr();
  in->setExpr(SQLUtils::toSQLExpr(column));

  for (Object_ptr value : *values)
  {
    in->addTarget(fromJavaObject(value, timeZone));
  }
  return in;
}

string_ptr SQLExprUtils::quote(string_ptr str, DbType_ptr dbType, char quote)
{
  SQLExpr_ptr expr;
  if (quote == '`')
  {
    expr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(str));
  }
  else if (quote == '"')
  {
    if (*dbType == DbType::oracle || *dbType == DbType::presto || *dbType == DbType::trino)
    {
      expr = SQLIdentifierExpr_ptr(new SQLIdentifierExpr(str));
    }
    else
    {
      expr = SQLCharExpr_ptr(new SQLCharExpr(str));
    }
  }
  else if (quote == '\'')
  {
    expr = SQLCharExpr_ptr(new SQLCharExpr(str));
  }
  else
  {
    throw new FastsqlException(make_string_ptr("quote not support"));
  }

  return SQLUtils::toSQLString(std::dynamic_pointer_cast< SQLObject>(expr), dbType);
}

SQLDataType_ptr SQLExprUtils::createDataTypeFromJdbc(DbType_ptr dbType, int jdbType, int precision, int scale)
{
  SQLDataType_ptr dataType = nullptr;

  switch (jdbType)
  {
  case Types::INTEGER:
    if (dbType == nullptr)
    {
      string_ptr integer = make_string_ptr("integer");
      return SQLDataType_ptr(new SQLDataTypeImpl(integer));
    }

    if (*dbType == DbType::mysql)
    {
      string_ptr int_ = make_string_ptr("int");
      return SQLDataType_ptr(new SQLDataTypeImpl(int_));
    }
    break;
  case Types::VARCHAR:
    if (*dbType == DbType::mysql)
    {
      string_ptr varchar = make_string_ptr("varchar");
      return SQLDataType_ptr(new SQLDataTypeImpl(varchar));
    }
    break;
  default:
    break;
  }

  if (dataType != nullptr)
  {
    if (dbType != nullptr)
    {
      dataType->setDbType(dbType);
    }

    return dataType;
  }
  throw new FastsqlException(make_string_ptr("type " + std::to_string(jdbType) + " not support"));
}
